#if UNITY_2019_3_OR_NEWER
using System;
using System.Collections.Generic;
using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
#endif


namespace RGuang.Kit
{
    [DisallowMultipleComponent]
    public sealed class GameObjectPool : MonoBehaviour
    {
        /// <summary>
        /// 默认容量
        /// </summary>
        public const uint DefaultCapacity = 10;

        #region ---  Properties  ---
        private Queue<GameObject> m_idlePool = new();
        private List<GameObject> m_usePool = new();

        public string PoolName { get => m_poolName; private set => m_poolName = value; }
        [SerializeField] private string m_poolName;
        public GameObject Prefab { get => m_prefab; private set => m_prefab = value; }
        [SerializeField] private GameObject m_prefab;
        public Transform ParentRoot { get => m_parentRoot; private set => m_parentRoot = value; }
        [SerializeField] private Transform m_parentRoot;
        public uint ConfigCapacity { get => m_configCapacity; private set => m_configCapacity = value; }
        [SerializeField] private uint m_configCapacity;
        public int ReleaseIdleCount { get; private set; }
        public int CreatedCount { get; private set; }
        public int IdleCount { get => m_idlePool.Count; }
        public int UseCount { get => m_usePool.Count; }
        public int OverflowCount { get; private set; }
        public long CreatedPoolTime { get; private set; }
        public long LastCreatedObjTime { get; private set; }
        public long LastSpawnObjTime { get; private set; }
        public long LastUnSpawnObjTime { get; private set; }
        public bool Initialized { get; private set; }
        #endregion

        private void Awake()
        {
            if (Prefab != null)
            {
                PoolName ??= Prefab.name;
                ParentRoot ??= transform;
                InitPool(Prefab, ParentRoot, PoolName, ConfigCapacity);
            }
        }

        #region --- Init/Dispose ---
        /// <summary>
        /// 
        /// </summary>
        /// <param name="prefab">对象预制体</param>
        /// <param name="parentRoot">创建对象的根节点</param>
        /// <param name="poolName">对象池名</param>
        /// <param name="poolCapacity">对象池容量</param>
        /// <param name="releaseIdleInterval">释放闲置资源间隔【秒】</param>
        /// <returns></returns>
        public GameObjectPool InitPool(
            GameObject prefab,
            Transform parentRoot,
            string poolName = null,
            uint poolCapacity = DefaultCapacity)
        {
            if (Initialized)
            {
#if UNITY_EDITOR
                Debug.LogError($"重复Init！[{PoolName}]对象池已初始化。");
#endif
                return this;
            }

            Prefab = prefab;
            ParentRoot = parentRoot;
            if (string.IsNullOrWhiteSpace(poolName)) PoolName = Prefab.name;
            else PoolName = poolName;

            ConfigCapacity = poolCapacity;
            m_idlePool = new Queue<GameObject>((int)ConfigCapacity);
            m_usePool = new List<GameObject>((int)ConfigCapacity);

            ReleaseIdleCount = 0;
            CreatedCount = 0;
            OverflowCount = 0;
            CreatedPoolTime = DateTime.UtcNow.ToLocalTime().Ticks;
            LastCreatedObjTime = CreatedPoolTime;
            LastSpawnObjTime = CreatedPoolTime;
            LastUnSpawnObjTime = CreatedPoolTime;

            Initialized = true;
            return this;
        }

        /// <summary>
        /// 释放闲置资源
        /// </summary>
        public void ClearIdle()
        {
            if (Initialized == false) return;

            List<GameObject> lst = new List<GameObject>(IdleCount);
            lock (m_idlePool)
            {
                lst.AddRange(m_idlePool.ToArray());
                m_idlePool.Clear();
            }

            for (int i = 0; i < lst.Count; i++)
            {
                var obj = lst[i];
                if (!obj) continue;
                GameObject.Destroy(obj);
                ReleaseIdleCount++;
            }

        }

        public void DisposePool()
        {
            if (Initialized)
            {
                Initialized = false;
                List<GameObject> lst = new List<GameObject>(IdleCount + UseCount);
                lock (m_idlePool)
                {
                    lst.AddRange(m_idlePool.ToArray());
                    m_idlePool.Clear();
                }
                lock (m_usePool)
                {
                    lst.AddRange(m_usePool.ToArray());
                    m_usePool.Clear();
                }

                for (int i = 0; i < lst.Count; i++)
                {
                    var obj = lst[i];
                    if (obj != null) GameObject.Destroy(obj);
                }

            }
            else
            {
#if UNITY_EDITOR
                Debug.LogError($"对象池重复Dispose！[{PoolName}]对象池已释放。");
#endif
            }
        }
        #endregion


        #region --- Private Function ---
        private GameObject CreateObj()
        {
            var copy = GameObject.Instantiate(Prefab, ParentRoot);
            copy.SetActive(false);
            CreatedCount++;
            LastCreatedObjTime = DateTime.UtcNow.ToLocalTime().Ticks;
            return copy;
        }

        private GameObject AvailableObject()
        {
            GameObject obj = null;

            lock (m_idlePool) { m_idlePool.TryDequeue(out obj); }

            if (!obj) obj = CreateObj();

            lock (m_usePool) { m_usePool.Add(obj); }

            LastSpawnObjTime = DateTime.UtcNow.ToLocalTime().Ticks;

            return obj;
        }
        #endregion


        #region --- Spawn、UnSpawn ---
        /// <summary>
        /// 取一个可用对象
        /// </summary>
        /// <param name="pos">预设该对象本地位置</param>
        /// <param name="rot">旋转</param>
        /// <param name="localScale">缩放</param>
        /// <param name="active">激活状态</param>
        /// <param name="parent">父级</param>
        /// <returns></returns>
        public GameObject Spawn(Vector3? pos = null, Quaternion? rot = null, Vector3? localScale = null, bool active = true, Transform parent = null)
        {
            pos ??= Vector3.zero;
            rot ??= Quaternion.identity;
            localScale ??= Vector3.one;
            parent ??= ParentRoot;

            GameObject obj = AvailableObject();
            if (obj.transform.parent.Equals(parent) == false) obj.transform.SetParent(parent);
            if (obj.activeSelf != active) obj.SetActive(active);

            obj.transform.localPosition = pos.Value;
            obj.transform.localRotation = rot.Value;
            obj.transform.localScale = localScale.Value;
            return obj;
        }

        /// <summary>
        /// 回收
        /// </summary>
        /// <param name="obj"></param>
        /// <exception cref="Exception">可能重复回收</exception>
        public void UnSpawn(GameObject obj)
        {
            if (obj == null) return;
            if (obj.activeSelf) obj.SetActive(false);
            obj.transform.SetParent(ParentRoot);

            lock (m_usePool) { m_usePool.Remove(obj); }

            if (m_idlePool.Contains(obj))
            {
                throw new Exception($"对象池[{PoolName}] 重复回收对象{obj}");
            }


            if ((IdleCount + UseCount) >= ConfigCapacity)
            {
                OverflowCount++;
                GameObject.Destroy(obj);
            }
            else
            {
                lock (m_idlePool) { m_idlePool.Enqueue(obj); }
            }

            LastUnSpawnObjTime = DateTime.UtcNow.ToLocalTime().Ticks;
        }

        /// <summary>
        /// 检测对象是否可回收
        /// </summary>
        /// <param name="obj"></param>
        /// <returns>返回True则可回收，返回False则对象已存在池子中</returns>
        public bool CanUnSpawn(GameObject obj) => m_idlePool.Contains(obj) == false;
        #endregion



    }


    /**  -------------------     Editor     ---------------------  */

#if UNITY_EDITOR
    [CustomEditor(typeof(GameObjectPool))]
    internal sealed class GameObjectPoolInspector : UnityEditor.Editor
    {
        private SerializedProperty m_poolName;
        private SerializedProperty m_prefab;
        private SerializedProperty m_parentRoot;
        private SerializedProperty m_capacity;

        private readonly GUIContent label_poolName = new GUIContent("池子名");
        private readonly GUIContent label_prefab = new GUIContent("预制体");
        private readonly GUIContent label_parentRoot = new GUIContent("根节点（默认父级）");
        private readonly GUIContent label_capacity = new GUIContent("配置容量");

        private readonly GUIStyle style_defaultTxt = new GUIStyle();
        private readonly GUIStyle style_createdCount = new GUIStyle();
        private readonly GUIStyle style_overflowCount = new GUIStyle();
        private readonly GUIStyle style_usingCount = new GUIStyle();
        private readonly GUIStyle style_IdleCount = new GUIStyle();

        public override void OnInspectorGUI()
        {
            //base.OnInspectorGUI();
            serializedObject.Update();

            GameObjectPool t = (GameObjectPool)target;

            EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
            {
                EditorGUILayout.PropertyField(m_poolName, label_poolName);
                EditorGUILayout.PropertyField(m_prefab, label_prefab);
                EditorGUILayout.PropertyField(m_parentRoot, label_parentRoot);
                EditorGUILayout.PropertyField(m_capacity, label_capacity);
                EditorGUILayout.LabelField($"对象池创建时间: {new DateTime(t.CreatedPoolTime)}");

                EditorGUILayout.Space(5);
                EditorGUILayout.LabelField($"已创建数量: {t.CreatedCount}", style_createdCount);
                EditorGUILayout.LabelField($"闲置数量: {t.IdleCount}", style_IdleCount);
                EditorGUILayout.LabelField($"正在使用数量: {t.UseCount}", style_usingCount);
                EditorGUILayout.LabelField($"溢出数量: {t.OverflowCount}", style_overflowCount);
                EditorGUILayout.LabelField($"自动释放闲置的资源数: {t.ReleaseIdleCount}");

                EditorGUILayout.Space(5);
                EditorGUILayout.LabelField($"上一次创建对象时间: {new DateTime(t.LastCreatedObjTime)}");
                EditorGUILayout.LabelField($"上一次取用对象时间: {new DateTime(t.LastSpawnObjTime)}");
                EditorGUILayout.LabelField($"上一次回收对象时间: {new DateTime(t.LastUnSpawnObjTime)}");
            }
            EditorGUI.EndDisabledGroup();

            serializedObject.ApplyModifiedProperties();
            //Repaint();
        }


        private void OnEnable()
        {
            m_poolName = serializedObject.FindProperty("m_poolName");
            m_prefab = serializedObject.FindProperty("m_prefab");
            m_parentRoot = serializedObject.FindProperty("m_parentRoot");
            m_capacity = serializedObject.FindProperty("m_configCapacity");

            /** -- Label -- */
            //style_createdCount.normal.textColor = new Color(0.80f, 0.30f, 0.30f);//Red
            //style_createdCount.normal.textColor = new Color(0.30f, 0.80f, 0.30f);//Green
            //style_createdCount.normal.textColor = new Color(0.30f, 0.30f, 0.80f);//Blue
            //style_createdCount.normal.textColor = new Color(0.60f, 0.60f, 0.0f);//DarkYellow
            //style_createdCount.normal.textColor = new Color(0.0f, 0.60f, 0.60f);//DarkCyan
            style_defaultTxt.normal.textColor = Color.white;
            style_defaultTxt.fontSize = 13;
            style_createdCount.normal.textColor = Color.white;
            style_createdCount.fontSize = 13;
            style_overflowCount.normal.textColor = new Color(0.60f, 0.60f, 0.0f);
            style_overflowCount.fontSize = 13;
            style_usingCount.normal.textColor = new Color(0.0f, 0.60f, 0.60f);
            style_usingCount.fontSize = 13;
            style_IdleCount.normal.textColor = new Color(0.30f, 0.80f, 0.30f);
            style_IdleCount.fontSize = 13;

        }
    }
#endif


#endif

}


